﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Interfaces;
using WPFCABShell.Shell.Interface.Services.Command;
using WPFCABShell.Shell.Services.Command;
using CAB = Microsoft.Practices.CompositeUI.Commands;
using WPF = System.Windows.Input;

namespace WPFCABShell.UnitTest.Shell.Services.Command
{
	[TestFixture]
	public sealed class CommandServiceTest : UnitTest.Shared.UnitTest
	{
		private CommandService _commandService;
		private CommandEventArgs _commandRegisteredEventArgs;
		private CommandEventArgs _commandUnregisteredEventArgs;
		private CommandEventArgs _commandExecutedEventArgs;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			ClearEvents();
			_commandService = new CommandService();

			_commandService.CommandRegistered += delegate(object sender, CommandEventArgs e)
			{
				_commandRegisteredEventArgs = e;
			};

			_commandService.CommandUnregistered += delegate(object sender, CommandEventArgs e)
			{
				_commandUnregisteredEventArgs = e;
			};

			_commandService.CommandExecuted += delegate(object sender, CommandEventArgs e)
			{
				_commandExecutedEventArgs = e;
			};
		}

		[Test]
		public void RegisteredCommands_ShouldBeEmptyByDefault()
		{
			Assert.IsEmpty(_commandService.RegisteredCommands as System.Collections.ICollection);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateCommand_ShouldThrowIfNameNull()
		{
			_commandService.CreateCommand(null, null);
		}

		[Test]
		public void CreateCommand_ShouldCreateNullCommand()
		{
			ICommand command = _commandService.CreateCommand("Name", null);
			Assert.IsNotNull(command);
			Assert.IsNotNull(command.Id);
			Assert.AreEqual("Name", command.Name);
			Assert.IsTrue(command.IsNullCommand);
			//can execute is true so that UI element appears enabled
			Assert.IsTrue(command.CanExecute(null));
		}

		[Test]
		public void CreateCommand_ExecutingCommandShouldNotRaiseCommandExecutedEvent()
		{
			Assert.IsNull(_commandExecutedEventArgs);
			ICommand command = _commandService.CreateCommand("Name", null);
			command.Execute(null);
			//still null - you can't execute a null command
			Assert.IsNull(_commandExecutedEventArgs);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateCommand_CAB_ShouldThrowIfCommandNull()
		{
			_commandService.CreateCommand((CAB.Command)null, "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateCommand_CAB_ShouldThrowIfNullNull()
		{
			CAB.Command cabCommand = new CAB.Command();
			_commandService.CreateCommand(cabCommand, null, null);
		}

		[Test]
		public void CreateCommand_CAB_ShouldCreateCommandForCabCommand()
		{
			CAB.Command cabCommand = new CAB.Command();
			ICommand command = _commandService.CreateCommand(cabCommand, "name", null);
			Assert.IsNotNull(command);
			Assert.IsNotNull(command.Id);
			Assert.IsNull(command.Category);
			Assert.AreEqual("name", command.Name);
			Assert.IsTrue(command.CanExecute(null));
			Assert.IsFalse(command.IsNullCommand);

			//shouldn't register command
			Assert.IsEmpty(_commandService.RegisteredCommands as System.Collections.ICollection);
		}

		[Test]
		public void CreateCommand_CAB_ExecutingCommandShouldRaiseCommandExecutedEvent()
		{
			Assert.IsNull(_commandExecutedEventArgs);
			CAB.Command cabCommand = new CAB.Command();
			ICommand command = _commandService.CreateCommand(cabCommand, "Temp", null);
			command.Execute(null);
			Assert.IsNotNull(_commandExecutedEventArgs);
			Assert.AreSame(command, _commandExecutedEventArgs.Command);
		}

		[Test]
		public void CreateCommand_CAB_ChangeInCABCommandStatusShouldRaiseCanExecuteChanged()
		{
			CAB.Command cabCommand = new CAB.Command();
			ICommand command = _commandService.CreateCommand(cabCommand, "Temp", null);
			bool raised = false;

			command.CanExecuteChanged += delegate(object sender, EventArgs e)
			{
				raised = true;
			};

			cabCommand.Status = Microsoft.Practices.CompositeUI.Commands.CommandStatus.Disabled;
			Assert.IsTrue(raised);

			raised = false;
			cabCommand.Status = Microsoft.Practices.CompositeUI.Commands.CommandStatus.Disabled;
			Assert.IsFalse(raised);

			cabCommand.Status = Microsoft.Practices.CompositeUI.Commands.CommandStatus.Enabled;
			Assert.IsTrue(raised);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateCommand_WPF_ShouldThrowIfCommandNull()
		{
			_commandService.CreateCommand((WPF.ICommand)null, "name", null);
		}

		[Test]
		public void CreateCommand_WPF_ShouldCreateCommandForCabCommand()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateCommand(wpfCommand, "name", null);
			Assert.IsNotNull(command);
			Assert.IsNotNull(command.Id);
			Assert.IsNull(command.Category);
			Assert.AreEqual("name", command.Name);
			Assert.IsFalse(command.CanExecute(null));
			Assert.IsFalse(command.IsNullCommand);

			//shouldn't register command
			Assert.IsEmpty(_commandService.RegisteredCommands as System.Collections.ICollection);
		}

		[Test]
		public void CreateCommand_WPF_ShouldUseRoutedUICommandNameIfNecessary()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateCommand(wpfCommand, null, null);
			Assert.AreEqual("Close", command.Name);
		}

		[Test]
		public void CreateCommand_WPF_ExecutingCommandShouldRaiseCommandExecutedEvent()
		{
			Assert.IsNull(_commandExecutedEventArgs);
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateCommand(wpfCommand, "Temp", null);
			command.Execute(null);
			Assert.IsNotNull(_commandExecutedEventArgs);
			Assert.AreSame(command, _commandExecutedEventArgs.Command);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_CAB_ShouldThrowIfCommandNull()
		{
			_commandService.CreateAndRegisterCommand((CAB.Command)null, "id", "category", "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_CAB_ShouldThrowIfIdNull()
		{
			CAB.Command cabCommand = new CAB.Command();
			_commandService.CreateAndRegisterCommand(cabCommand, null, "category", "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_CAB_ShouldThrowIfCategoryNull()
		{
			CAB.Command cabCommand = new CAB.Command();
			_commandService.CreateAndRegisterCommand(cabCommand, "id", null, "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_CAB_ShouldThrowIfNameNull()
		{
			CAB.Command cabCommand = new CAB.Command();
			_commandService.CreateAndRegisterCommand(cabCommand, "id", "category", null, null);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "A command with ID 'id' is already registered.")]
		public void CreateAndRegisterCommand_CAB_ShouldThrowIfCommandAlreadyRegistered()
		{
			CAB.Command cabCommand = new CAB.Command();
			_commandService.CreateAndRegisterCommand(cabCommand, "id", "category", "name", null);
			_commandService.CreateAndRegisterCommand(cabCommand, "id", "category", "name", null);
		}

		[Test]
		public void CreateAndRegisterCommand_CAB_ShouldCreateAndRegisterCommand()
		{
			Assert.IsNull(_commandRegisteredEventArgs);
			CAB.Command cabCommand = new CAB.Command();
			ICommand command = _commandService.CreateAndRegisterCommand(cabCommand, "id", "category", "name", null);
			Assert.IsNotNull(command);
			Assert.AreEqual("id", command.Id);
			Assert.AreEqual("category", command.Category);
			Assert.AreEqual("name", command.Name);
			Assert.IsNotNull(_commandRegisteredEventArgs);
			Assert.AreSame(command, _commandRegisteredEventArgs.Command);
			Assert.IsFalse(command.IsNullCommand);

			//should register command
			Assert.AreEqual(1, _commandService.RegisteredCommands.Count);
			Assert.AreSame(command, _commandService.RegisteredCommands[0]);
		}

		[Test]
		public void CreateAndRegisterCommand_CAB_ExecutingCommandShouldRaiseCommandExecutedEvent()
		{
			Assert.IsNull(_commandExecutedEventArgs);
			CAB.Command cabCommand = new CAB.Command();
			ICommand command = _commandService.CreateAndRegisterCommand(cabCommand, "id", "category", "name", null);
			command.Execute(null);
			Assert.IsNotNull(_commandExecutedEventArgs);
			Assert.AreSame(command, _commandExecutedEventArgs.Command);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_WPF_ShouldThrowIfCommandNull()
		{
			_commandService.CreateAndRegisterCommand((WPF.ICommand)null, "id", "category", "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_WPF_ShouldThrowIfIdNull()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			_commandService.CreateAndRegisterCommand(wpfCommand, null, "category", "name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAndRegisterCommand_WPF_ShouldThrowIfCategoryNull()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			_commandService.CreateAndRegisterCommand(wpfCommand, "id", null, "name", null);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage="A command with ID 'id' is already registered.")]
		public void CreateAndRegisterCommand_WPF_ShouldThrowIfCommandAlreadyRegistered()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			_commandService.CreateAndRegisterCommand(wpfCommand, "id", "category", "name", null);
			_commandService.CreateAndRegisterCommand(wpfCommand, "id", "category", "name", null);
		}

		[Test]
		public void CreateAndRegisterCommand_WPF_ShouldCreateAndRegisterCommand()
		{
			Assert.IsNull(_commandRegisteredEventArgs);
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateAndRegisterCommand(wpfCommand, "id", "category", "name", null);
			Assert.IsNotNull(command);
			Assert.AreEqual("id", command.Id);
			Assert.AreEqual("category", command.Category);
			Assert.AreEqual("name", command.Name);
			Assert.IsNotNull(_commandRegisteredEventArgs);
			Assert.AreSame(command, _commandRegisteredEventArgs.Command);
			Assert.IsFalse(command.IsNullCommand);

			//should register command
			Assert.AreEqual(1, _commandService.RegisteredCommands.Count);
			Assert.AreSame(command, _commandService.RegisteredCommands[0]);
		}

		[Test]
		public void CreateAndRegisterCommand_WPF_ShouldUseRoutedUICommandNameIfNecessary()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateAndRegisterCommand(wpfCommand, "id", "category", null, null);
			Assert.AreEqual("Close", command.Name);
		}

		[Test]
		public void CreateAndRegisterCommand_WPF_ExecutingCommandShouldRaiseCommandExecutedEvent()
		{
			Assert.IsNull(_commandExecutedEventArgs);
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			ICommand command = _commandService.CreateAndRegisterCommand(wpfCommand, "id", "category", "name", null);
			command.Execute(null);
			Assert.IsNotNull(_commandExecutedEventArgs);
			Assert.AreSame(command, _commandExecutedEventArgs.Command);
		}

		[Test]
		public void UnregisterCommand_ShouldUnregisterGivenCommand()
		{
			WPF.ICommand wpfCommand = WPF.ApplicationCommands.Close;
			CAB.Command cabCommand = new CAB.Command();

			ICommand command1 = _commandService.CreateAndRegisterCommand(wpfCommand, "id1", "category", "command1", null);
			ICommand command2 = _commandService.CreateAndRegisterCommand(cabCommand, "id2", "category", "command2", null);

			Assert.AreEqual(2, _commandService.RegisteredCommands.Count);
			Assert.IsNull(_commandUnregisteredEventArgs);

			_commandService.UnregisterCommand(command1);
			Assert.AreEqual(1, _commandService.RegisteredCommands.Count);
			Assert.AreSame(command2, _commandService.RegisteredCommands[0]);
			Assert.IsNotNull(_commandUnregisteredEventArgs);
			Assert.AreSame(command1, _commandUnregisteredEventArgs.Command);

			ClearEvents();

			//should do nothing if not found
			_commandService.UnregisterCommand(command1);
			Assert.AreEqual(1, _commandService.RegisteredCommands.Count);
			Assert.AreSame(command2, _commandService.RegisteredCommands[0]);
			Assert.IsNull(_commandUnregisteredEventArgs);

			_commandService.UnregisterCommand(command2);
			Assert.IsEmpty(_commandService.RegisteredCommands as System.Collections.ICollection);
			Assert.IsNotNull(_commandUnregisteredEventArgs);
			Assert.AreSame(command2, _commandUnregisteredEventArgs.Command);
		}

		#region Supporting Methods

		private void ClearEvents()
		{
			_commandRegisteredEventArgs = null;
			_commandUnregisteredEventArgs = null;
			_commandExecutedEventArgs = null;
		}

		#endregion
	}
}
